package com.geo.content.service;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.geo.content.entity.MediaFile;
import com.geo.content.mapper.MediaFileMapper;
import com.geo.content.storage.StorageService;
import lombok.extern.slf4j.Slf4j;
import net.bramp.ffmpeg.FFmpeg;
import net.bramp.ffmpeg.FFmpegExecutor;
import net.bramp.ffmpeg.FFprobe;
import net.bramp.ffmpeg.builder.FFmpegBuilder;
import net.bramp.ffmpeg.probe.FFmpegProbeResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 媒体处理服务
 * 处理图片、视频、音频文件的压缩、转换、缩略图生成等
 * 
 * @author GEO Team
 * @version 1.0
 */
@Slf4j
@Service
public class MediaProcessingService {
    
    private static final Logger log = LoggerFactory.getLogger(MediaProcessingService.class);
    
    @Autowired
    private MediaFileMapper mediaFileMapper;
    
    @Autowired
    private StorageService storageService;
    
    // FFmpeg相关组件
    private FFmpeg ffmpeg;
    private FFprobe ffprobe;
    
    // 缩略图尺寸配置
    private static final int THUMBNAIL_WIDTH = 300;
    private static final int THUMBNAIL_HEIGHT = 200;
    
    // 视频压缩配置
    private static final String VIDEO_CODEC = "libx264";
    private static final String AUDIO_CODEC = "aac";
    private static final int VIDEO_BITRATE = 1000; // kbps
    private static final int AUDIO_BITRATE = 128; // kbps
    
    /**
     * 初始化FFmpeg组件
     */
    private void initFFmpeg() {
        try {
            if (ffmpeg == null) {
                ffmpeg = new FFmpeg("ffmpeg"); // 需要系统中安装FFmpeg
                ffprobe = new FFprobe("ffprobe");
            }
        } catch (Exception e) {
            log.warn("FFmpeg初始化失败，视频/音频处理功能将不可用: {}", e.getMessage());
        }
    }
    
    /**
     * 异步处理媒体文件
     * @param mediaFile 媒体文件
     */
    @Async
    public void processMediaFileAsync(MediaFile mediaFile) {
        try {
            log.info("开始处理媒体文件: {}", mediaFile.getId());
            
            // 更新处理状态
            updateProcessStatus(mediaFile.getId(), MediaFile.ProcessStatus.PROCESSING);
            
            // 根据文件类型进行不同的处理
            switch (mediaFile.getFileType()) {
                case IMAGE -> processImage(mediaFile);
                case VIDEO -> processVideo(mediaFile);
                case AUDIO -> processAudio(mediaFile);
                default -> {
                    log.info("文件类型无需特殊处理: {}", mediaFile.getFileType());
                    updateProcessStatus(mediaFile.getId(), MediaFile.ProcessStatus.SKIPPED);
                    return;
                }
            }
            
            updateProcessStatus(mediaFile.getId(), MediaFile.ProcessStatus.COMPLETED);
            log.info("媒体文件处理完成: {}", mediaFile.getId());
            
        } catch (Exception e) {
            log.error("媒体文件处理失败: {}", mediaFile.getId(), e);
            updateProcessStatus(mediaFile.getId(), MediaFile.ProcessStatus.FAILED);
            updateProcessResult(mediaFile.getId(), "处理失败: " + e.getMessage());
        }
    }
    
    /**
     * 处理图片文件
     * @param mediaFile 图片文件
     */
    private void processImage(MediaFile mediaFile) throws IOException {
        log.info("处理图片文件: {}", mediaFile.getId());
        
        // 下载原图
        byte[] imageData = storageService.downloadFile(mediaFile.getFilePath());
        BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(imageData));
        
        if (originalImage == null) {
            throw new IOException("无法读取图片文件");
        }
        
        // 获取图片信息
        Map<String, Object> mediaInfo = new HashMap<>();
        mediaInfo.put("width", originalImage.getWidth());
        mediaInfo.put("height", originalImage.getHeight());
        mediaInfo.put("format", getImageFormat(mediaFile.getFileName()));
        mediaInfo.put("hasTransparency", hasTransparency(originalImage));
        
        // 生成缩略图
        BufferedImage thumbnail = createThumbnail(originalImage, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);
        String thumbnailPath = generateThumbnailPath(mediaFile.getFilePath());
        String thumbnailUrl = uploadThumbnail(thumbnail, thumbnailPath, "image/jpeg");
        
        // 更新媒体文件信息
        mediaFile.setMediaInfo(JSONUtil.toJsonStr(mediaInfo));
        mediaFile.setThumbnailPath(thumbnailPath);
        mediaFile.setThumbnailUrl(thumbnailUrl);
        
        mediaFileMapper.updateById(mediaFile);
        
        log.info("图片处理完成，尺寸: {}x{}", originalImage.getWidth(), originalImage.getHeight());
    }
    
    /**
     * 处理视频文件
     * @param mediaFile 视频文件
     */
    private void processVideo(MediaFile mediaFile) throws Exception {
        log.info("处理视频文件: {}", mediaFile.getId());
        
        initFFmpeg();
        if (ffmpeg == null || ffprobe == null) {
            throw new RuntimeException("FFmpeg未正确初始化");
        }
        
        // 下载视频文件到临时目录
        byte[] videoData = storageService.downloadFile(mediaFile.getFilePath());
        Path tempVideoPath = Files.createTempFile("video_", ".tmp");
        Files.write(tempVideoPath, videoData);
        
        try {
            // 获取视频信息
            FFmpegProbeResult probeResult = ffprobe.probe(tempVideoPath.toString());
            
            Map<String, Object> mediaInfo = extractVideoInfo(probeResult);
            
            // 生成视频缩略图（第1秒的帧）
            String thumbnailPath = generateThumbnailPath(mediaFile.getFilePath());
            Path tempThumbnailPath = Files.createTempFile("thumbnail_", ".jpg");
            
            FFmpegBuilder builder = new FFmpegBuilder()
                    .setInput(tempVideoPath.toString())
                    .overrideOutputFiles(true)
                    .addOutput(tempThumbnailPath.toString())
                    .setFormat("image2")
                    .setVideoCodec("mjpeg")
                    .setFrames(1)
                    .setVideoFilter("scale=" + THUMBNAIL_WIDTH + ":" + THUMBNAIL_HEIGHT)
                    .done();
            
            FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe);
            executor.createJob(builder).run();
            
            // 上传缩略图
            byte[] thumbnailData = Files.readAllBytes(tempThumbnailPath);
            String thumbnailUrl = storageService.uploadFile(
                    new ByteArrayInputStream(thumbnailData), 
                    thumbnailPath, 
                    "image/jpeg"
            );
            
            // 更新媒体文件信息
            mediaFile.setMediaInfo(JSONUtil.toJsonStr(mediaInfo));
            mediaFile.setThumbnailPath(thumbnailPath);
            mediaFile.setThumbnailUrl(thumbnailUrl);
            
            mediaFileMapper.updateById(mediaFile);
            
            // 清理临时文件
            Files.deleteIfExists(tempThumbnailPath);
            
            log.info("视频处理完成，时长: {}秒", mediaInfo.get("duration"));
            
        } finally {
            Files.deleteIfExists(tempVideoPath);
        }
    }
    
    /**
     * 处理音频文件
     * @param mediaFile 音频文件
     */
    private void processAudio(MediaFile mediaFile) throws Exception {
        log.info("处理音频文件: {}", mediaFile.getId());
        
        initFFmpeg();
        if (ffmpeg == null || ffprobe == null) {
            throw new RuntimeException("FFmpeg未正确初始化");
        }
        
        // 下载音频文件到临时目录
        byte[] audioData = storageService.downloadFile(mediaFile.getFilePath());
        Path tempAudioPath = Files.createTempFile("audio_", ".tmp");
        Files.write(tempAudioPath, audioData);
        
        try {
            // 获取音频信息
            FFmpegProbeResult probeResult = ffprobe.probe(tempAudioPath.toString());
            
            Map<String, Object> mediaInfo = extractAudioInfo(probeResult);
            
            // 更新媒体文件信息
            mediaFile.setMediaInfo(JSONUtil.toJsonStr(mediaInfo));
            mediaFileMapper.updateById(mediaFile);
            
            log.info("音频处理完成，时长: {}秒", mediaInfo.get("duration"));
            
        } finally {
            Files.deleteIfExists(tempAudioPath);
        }
    }
    
    /**
     * 创建缩略图
     * @param originalImage 原图
     * @param width 缩略图宽度
     * @param height 缩略图高度
     * @return 缩略图
     */
    private BufferedImage createThumbnail(BufferedImage originalImage, int width, int height) {
        // 计算缩放比例，保持纵横比
        double scaleX = (double) width / originalImage.getWidth();
        double scaleY = (double) height / originalImage.getHeight();
        double scale = Math.min(scaleX, scaleY);
        
        int scaledWidth = (int) (originalImage.getWidth() * scale);
        int scaledHeight = (int) (originalImage.getHeight() * scale);
        
        BufferedImage thumbnail = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = thumbnail.createGraphics();
        
        // 设置高质量渲染
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        // 填充白色背景
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, width, height);
        
        // 居中绘制缩放后的图片
        int x = (width - scaledWidth) / 2;
        int y = (height - scaledHeight) / 2;
        g2d.drawImage(originalImage, x, y, scaledWidth, scaledHeight, null);
        
        g2d.dispose();
        
        return thumbnail;
    }
    
    /**
     * 上传缩略图
     * @param thumbnail 缩略图
     * @param thumbnailPath 缩略图路径
     * @param mimeType MIME类型
     * @return 缩略图URL
     */
    private String uploadThumbnail(BufferedImage thumbnail, String thumbnailPath, String mimeType) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(thumbnail, "jpeg", baos);
        byte[] thumbnailData = baos.toByteArray();
        
        return storageService.uploadFile(
                new ByteArrayInputStream(thumbnailData), 
                thumbnailPath, 
                mimeType
        );
    }
    
    /**
     * 生成缩略图路径
     * @param originalPath 原文件路径
     * @return 缩略图路径
     */
    private String generateThumbnailPath(String originalPath) {
        String pathWithoutExt = StrUtil.subBefore(originalPath, ".", true);
        return pathWithoutExt + "_thumb.jpg";
    }
    
    /**
     * 获取图片格式
     * @param fileName 文件名
     * @return 图片格式
     */
    private String getImageFormat(String fileName) {
        String extension = StrUtil.subAfter(fileName, ".", true);
        return extension != null ? extension.toLowerCase() : "unknown";
    }
    
    /**
     * 检查图片是否有透明度
     * @param image 图片
     * @return 是否有透明度
     */
    private boolean hasTransparency(BufferedImage image) {
        return image.getColorModel().hasAlpha();
    }
    
    /**
     * 提取视频信息
     * @param probeResult FFmpeg探测结果
     * @return 视频信息
     */
    private Map<String, Object> extractVideoInfo(FFmpegProbeResult probeResult) {
        Map<String, Object> info = new HashMap<>();
        
        if (probeResult.getFormat() != null) {
            info.put("duration", probeResult.getFormat().duration);
            info.put("bitrate", probeResult.getFormat().bit_rate);
            info.put("format", probeResult.getFormat().format_name);
        }
        
        if (!probeResult.getStreams().isEmpty()) {
            var videoStream = probeResult.getStreams().get(0);
            info.put("width", videoStream.width);
            info.put("height", videoStream.height);
            info.put("codec", videoStream.codec_name);
            info.put("fps", videoStream.r_frame_rate);
        }
        
        return info;
    }
    
    /**
     * 提取音频信息
     * @param probeResult FFmpeg探测结果
     * @return 音频信息
     */
    private Map<String, Object> extractAudioInfo(FFmpegProbeResult probeResult) {
        Map<String, Object> info = new HashMap<>();
        
        if (probeResult.getFormat() != null) {
            info.put("duration", probeResult.getFormat().duration);
            info.put("bitrate", probeResult.getFormat().bit_rate);
            info.put("format", probeResult.getFormat().format_name);
        }
        
        if (!probeResult.getStreams().isEmpty()) {
            var audioStream = probeResult.getStreams().get(0);
            info.put("codec", audioStream.codec_name);
            info.put("sample_rate", audioStream.sample_rate);
            info.put("channels", audioStream.channels);
        }
        
        return info;
    }
    
    /**
     * 更新处理状态
     * @param fileId 文件ID
     * @param status 处理状态
     */
    private void updateProcessStatus(String fileId, MediaFile.ProcessStatus status) {
        MediaFile mediaFile = new MediaFile();
        mediaFile.setId(fileId);
        mediaFile.setProcessStatus(status);
        mediaFile.setUpdatedAt(LocalDateTime.now());
        mediaFileMapper.updateById(mediaFile);
    }
    
    /**
     * 更新处理结果
     * @param fileId 文件ID
     * @param result 处理结果
     */
    private void updateProcessResult(String fileId, String result) {
        MediaFile mediaFile = new MediaFile();
        mediaFile.setId(fileId);
        mediaFile.setProcessResult(result);
        mediaFile.setUpdatedAt(LocalDateTime.now());
        mediaFileMapper.updateById(mediaFile);
    }
}